Conversation
Add Lorah AI pair programming setup to manage the refactoring of lnk to a stow-like flag-based interface. Includes specification, task tracking, progress logs, and session prompts for initialization and implementation phases.
Create comprehensive task breakdown for stow-like CLI refactor with 24 testable requirements across 4 implementation phases: - Phase 1: Config file support (.lnkconfig, .lnkignore) - Phase 2: Options-based API (package-centric linking) - Phase 3: CLI rewrite (flag-based interface) - Phase 4: Internal function updates Document initial codebase inventory and refactoring plan summary.
Implement Phase 1 of CLI refactor - stow-like config file format: - Add .lnkconfig file discovery and parsing (CLI flags format) - Add .lnkignore file parsing (gitignore syntax) - Implement config discovery precedence chain - Add FlagConfig struct for new config format Discovery order: .lnkconfig in source dir → XDG config → ~/.lnkconfig Tasks completed: LoadConfig for .lnkconfig format, Parse .lnkignore file
Add MergedConfig struct and MergeFlagConfig() function to merge configuration from multiple sources with proper precedence: - Target: CLI flag > .lnkconfig > default (~) - Ignore patterns: Combined from all sources (built-in, config, .lnkignore, CLI) to allow maximum flexibility Extract getBuiltInIgnorePatterns() for reusability and add .lnkconfig and .lnkignore to built-in ignore patterns. Add comprehensive tests covering all merging scenarios including precedence verification, subdirectories, and combined sources. Completes Phase 1 (Config file support) of the CLI refactor.
Add LinkOptions struct to support the new package-based linking interface. This struct replaces the config-based API with fields for SourceDir, TargetDir, Packages, IgnorePatterns, and DryRun. Part of Phase 2 of the CLI refactor to support stow-like usage.
Add CreateLinksWithOptions function that handles package-based linking using LinkOptions struct. Includes helper collectPlannedLinksWithPatterns that uses ignore patterns directly instead of Config objects. Features: - Supports multiple packages in single operation - Handles package "." for flat repository structure - Handles nested package paths (e.g., "private/home") - Skips non-existent packages gracefully - Follows same 3-phase execution as CreateLinks Includes comprehensive unit tests covering single/multiple packages, ignore patterns, dry-run mode, and error cases.
Add RemoveLinksWithOptions function that removes symlinks for specific packages, supporting the new stow-like interface. Includes helper function findManagedLinksForPackages for filtering managed links by package source. - Add findManagedLinksForPackages to find symlinks pointing to specific packages - Implement RemoveLinksWithOptions with package-based filtering - Support multiple packages, dry-run mode, and "." for flat repos - Add createTestSymlink helper for test utilities - Add comprehensive unit tests (8 test cases)
Add StatusWithOptions function that displays symlink status for specific packages instead of using config-based mappings. Reuses existing display logic and findManagedLinksForPackages helper for consistency. Features: - Shows status for only specified packages - Supports multiple packages in single operation - Separates active and broken links - Supports JSON output format - Comprehensive unit tests with 8 test cases
Add PruneWithOptions function to remove broken symlinks using the new package-based API. Unlike the old PruneLinks function, this supports: - Optional packages (defaults to "." if none specified) - Multiple package targeting in a single operation - DryRun flag without confirmation prompts (consistent with other *WithOptions functions) Includes comprehensive unit tests covering edge cases like partial pruning, mixed active/broken links, and graceful handling of no broken links.
Update collectPlannedLinks to accept ignorePatterns directly instead of requiring Config and LinkMapping objects. This simplifies the function signature and aligns it with the new package-based API pattern used by collectPlannedLinksWithPatterns. Changes: - Update collectPlannedLinks signature to take ignorePatterns []string - Replace shouldIgnoreEntry call with direct MatchesPattern usage - Remove unused shouldIgnoreEntry helper function - Update CreateLinks call site to pass config.IgnorePatterns This refactoring maintains backward compatibility with the old config-based CreateLinks API while preparing for the new flag-based CLI.
…sed link discovery Add FindManagedLinksForSources function that finds symlinks pointing to specified source directories without requiring a Config object. This completes the package-based API for Phase 2 of the CLI refactor. The function walks a target directory, identifies symlinks that point to any of the provided source paths, and returns ManagedLink structs with broken link detection. Added comprehensive unit tests covering: - Single and multiple source directories - Broken link detection - System directory skipping - Relative and nested package paths - Empty sources handling
Replace subcommand-based routing (lnk create, lnk status) with stow-like flag-based interface (lnk -C, lnk -S, lnk home). Action flags (mutually exclusive): - -C/--create (default), -R/--remove, -S/--status, -P/--prune - -A/--adopt, -O/--orphan (placeholders for Phase 4) Directory flags: - -s/--source DIR (default: .) - -t/--target DIR (default: ~) Other flags: - --ignore PATTERN (repeatable) - -n/--dry-run, -v/--verbose, -q/--quiet - --no-color, -V/--version, -h/--help Packages are now positional arguments (at least one required for link operations). Examples: - lnk . (flat repo) - lnk home (nested repo) - lnk home private/home (multiple packages) Integrates with .lnkconfig and .lnkignore config files via MergeFlagConfig(). All *WithOptions functions now wired up to CLI action dispatch. BREAKING CHANGE: Removes subcommand interface. Old commands like 'lnk create' must now use 'lnk home' or 'lnk -C home'. Config files are now optional (CLI flags sufficient).
Add new package-based adopt functionality for the flag-based CLI interface: - AdoptOptions struct for passing adopt parameters - AdoptWithOptions function supporting multiple file adoption - CLI integration with -A/--adopt flag + package + file paths - Comprehensive unit tests (9 test cases) Usage: lnk -A <package> <file1> [file2...] [-s source] [-t target] [-n] Example: lnk -A home ~/.bashrc ~/.vimrc The new interface eliminates the need for config files and makes adoption more intuitive by explicitly specifying the package destination and files to adopt.
Add new options-based API for orphaning files from repository management, supporting the new flag-based CLI interface. This completes Phase 4 of the CLI refactor. Changes: - Add OrphanOptions struct with SourceDir, TargetDir, Paths, and DryRun - Implement OrphanWithOptions() function supporting multiple paths - Support orphaning directories (finds all managed links within) - Graceful error handling (continues on failures) - Add comprehensive unit tests (9 test cases) - Update CLI to use OrphanWithOptions for --orphan flag - Update help text to remove "not yet implemented" note
Updated all e2e tests to use the new flag-based CLI syntax: - Changed from subcommands (lnk create) to action flags (lnk -C home) - Updated test assertions to match new command syntax and output - Adapted tests for macOS sandbox constraints that block dotfile creation - Fixed setupTestEnv to skip re-creating existing test files All e2e tests now pass with the new CLI interface. Tests verify: - Action flags: -C/--create, -R/--remove, -S/--status, -P/--prune, -A/--adopt, -O/--orphan - Directory flags: -s/--source, -t/--target - Package arguments and multiple package support - Error handling and edge cases Files modified: - e2e/e2e_test.go - Core command tests - e2e/workflows_test.go - Workflow and integration tests - e2e/helpers_test.go - Test setup helper - .lorah/tasks.json - Mark Task 23 as complete - .lorah/progress.md - Document session progress
Ran all 12 verification examples from spec.md to validate the new flag-based CLI interface. All examples work correctly: - Flat repo linking (lnk .) - Nested package operations (lnk home, lnk home private/home) - Explicit source/target flags - All action flags (-C, -R, -S, -P, -A, -O) - Config file integration (.lnkconfig and .lnkignore) - Adopt and orphan operations All 24 tasks are now complete: - Phase 1: Config file support (3 tasks) - Phase 2: Options-based API (7 tasks) - Phase 3: CLI rewrite (6 tasks) - Phase 4: Internal function updates (3 tasks) - Testing (5 tasks) Build status: All unit tests pass, all e2e tests pass, binary builds successfully.
Reset progress tracking and update spec/tasks for the next phase of work focused on removing legacy code retained from the CLI refactoring.
Create comprehensive task list and progress notes for removing ~900 lines of legacy code from the JSON config-based CLI. Tasks cover 6 phases: removing legacy functions, types, and constants; simplifying naming by dropping WithOptions suffixes; cleaning up tests; and updating documentation. All tasks marked as pending, ready for systematic cleanup execution.
Remove 9 legacy functions from config.go: - getDefaultConfig(), LoadConfig(), loadConfigFromFile() - LoadConfigWithOptions(), Config.Save(), Config.GetMapping() - Config.ShouldIgnore(), Config.Validate(), DetermineSourceMapping() Remove ensureSourceDirExists() from adopt.go (dead code). Also remove unused encoding/json import from config.go. These legacy functions used the old JSON config system with LinkMappings. The new flag-based system (MergeFlagConfig, LoadFlagConfig) remains. Test failures in config_test.go are expected and will be cleaned up in a later task.
Removed 4 legacy functions from linker.go (~200 lines): - CreateLinks(config *Config, dryRun bool) - RemoveLinks(config *Config, dryRun bool, force bool) - removeLinks(config *Config, dryRun bool, skipConfirm bool) - PruneLinks(config *Config, dryRun bool, force bool) These functions used the old Config struct and are replaced by CreateLinksWithOptions, RemoveLinksWithOptions, and PruneWithOptions.
Removed the legacy Status(config *Config) function from status.go as part of the cleanup effort. StatusWithOptions remains and will be renamed to Status in Phase 0. This removes 93 lines of legacy code. Only test files reference the removed function, which will be cleaned up in Task 25.
Remove Adopt(source string, config *Config, sourceDir string, dryRun bool) from adopt.go (98 lines). This function used the old JSON config system with LinkMappings. The new AdoptWithOptions() function remains and is used by the flag-based CLI interface. Test failures in adopt_test.go are expected and will be cleaned up in Task 26.
Removed Orphan(link string, config *Config, dryRun bool, force bool) from orphan.go (121 lines). Production code uses OrphanWithOptions instead.
Remove ErrNoLinkMappings error constant from errors.go as it was only used by the old JSON-based config system with LinkMappings. The new flag-based config system does not need this validation.
Removed ConfigFileName = ".lnk.json" constant from constants.go. This constant was part of the old JSON-based config system and is no longer used. The new flag-based config system uses FlagConfigFileName instead. Phase 1 (Remove Legacy Types and Constants) is now complete.
Removed 4 legacy test functions that used the old Config type with LinkMappings parameter pattern: - TestCreateLinks - TestRemoveLinks - TestPruneLinks - TestLinkerEdgeCases Kept 3 tests for the new LinkOptions-based API: - TestCreateLinksWithOptions - TestRemoveLinksWithOptions - TestPruneWithOptions File reduced from 1659 lines to 767 lines (892 lines removed).
- Fix ExpandPath to handle bare "~" path (not just "~/...") - Fix RemoveLinks and Prune to return errors on failures instead of only warning - Improve rollback error handling in adopt and orphan operations
The ValidateNoCircularSymlink function was ignoring filepath.Abs errors at lines 51-52 while properly handling them earlier in the same function. This could cause incorrect validation if filepath.Abs failed (e.g., when CWD is deleted), as empty strings would be used in path comparisons. Now errors are handled consistently throughout the function.
Fix goroutine leak in progress indicator by adding synchronization channel to ensure goroutine exits before Stop() returns, preventing output corruption. Track and report filesystem walk errors that were previously silently ignored, ensuring users are informed when operations encounter permission errors or other issues that result in incomplete results.
Previously, Adopt and CreateLinks returned nil even when some operations failed, causing scripts to incorrectly believe operations succeeded. Additionally, copyDir left partial files on failure, and orphanManagedLink rollback could fail after partial directory copies. Changes: - Adopt: track and return errors when adoptions fail - CreateLinks: return error when link creation fails - copyDir: clean up partial copies on all error paths - orphanManagedLink: clean up before rollback to ensure success This makes error handling consistent across all operations (Create, Remove, Prune, Adopt, Orphan) and ensures proper exit codes for scripting.
Remove over-abstracted code and redundant patterns: - Inline single-caller functions (printVersion, PrintHeader) - Remove redundant type assertion in GetErrorHint - Simplify return patterns in validation functions - Eliminate unnecessary intermediate variables - Remove always-false isBroken variable Net reduction: 30 lines (-38, +8)
Removed 94 lines by inlining helpers and eliminating duplication: - Deleted findManagedLinksForSource(), use FindManagedLinks() directly - Inlined getXDGConfigDir() into loadConfigFile() - Inlined GitCommandTimeout constant - Inlined performFileAdoption() into performAdoption()
MatchesPattern had exactly one production caller (linker.go) and created a new PatternMatcher on every call inside a filepath.Walk loop, causing unnecessary overhead. Pattern compilation now happens once before the walk, improving both code clarity and performance. - Remove MatchesPattern function from patterns.go (-7 lines) - Create PatternMatcher once in collectPlannedLinksWithPatterns (+4 lines) - Update tests to use PatternMatcher directly
Git operations are beyond the scope of lnk. The tool should focus on symlink management only, leaving git operations to users or external scripts. This simplifies the codebase and removes the external dependency on the git binary.
Remove all traces of old JSON config format (.lnk.json, config.json): - Delete unused test config files (config.json, invalid.json) - Remove unused helper functions (getConfigPath, getInvalidConfigPath) - Remove .lnk.json from default ignore patterns - Update setup script to use new .lnkconfig format - Update CHANGELOG to reflect current config discovery paths
JSON output was partially implemented but never completed - users had no way to enable it (no --output flag existed) and only the Status command had an implementation. This removes the dead code and corrects the documentation. - Delete format.go (unused OutputFormat enum) - Remove JSON structs and functions from status.go - Remove IsJSONFormat() checks from progress.go and terminal.go - Update CLAUDE.md to remove false --output json documentation
Move lnk package from internal/lnk to lnk and main.go from cmd/lnk to root for a simpler, flatter structure. Rename e2e tests to test directory. Split monolithic linker.go into focused create.go, remove.go, and prune.go modules. Rename link_utils.go to symlink.go for clarity.
Removed examples/lnk.json which was a remnant from when lnk supported JSON configuration. JSON config support was removed in commit 6189930, but the examples directory was not cleaned up. Current config system uses .lnkconfig files with stow-style format.
Create AGENTS.md as the primary documentation file and symlink CLAUDE.md to it, allowing the same documentation to be used by both Claude Code and other agent systems.
Replaced 6 identical action flag validation blocks with a single setAction() helper function. Reduces main.go by 30 lines while maintaining identical functionality and error handling.
Replace 70+ lines of helper function calls with a single well-aligned multiline string. Removes unnecessary complexity from PrintHelpSection and PrintHelpItems helpers that only provided bold formatting and dynamic column alignment - not worth the overhead for stable CLI help. - Consolidate printUsage() to use single fmt.Print() call - Remove unused PrintHelpSection() and PrintHelpItems() from output.go - Maintain identical output format with manual alignment
Adds specs/ directory with behavioral specifications for all lnk commands and subsystems. These are in-progress and will be revised as CLI interface decisions are finalized.
Relocate specification files from specs/ to docs/design/ to better organize documentation under a unified docs/ hierarchy.
Remove --source/--target flags in favor of positional arguments: all commands now require source-dir as the first positional arg, with target-dir as optional second for create/remove/status/prune, and one-or-more files as subsequent args for adopt/orphan.
Config system now has a single file format: gitignore-style .lnkignore loaded from the source directory. Target directory comes from the CLI positional arg only, defaulting to ~. Removes .lnkconfig format spec, XDG discovery logic, FileConfig type, and related precedence tables.
…ome dir Adopt and orphan no longer accept a --target flag or TargetDir option. Paths must reside within the user's home directory (~) instead of an arbitrary target directory. Updates adopt.md, orphan.md, cli.md, config.md, create.md, and error-handling.md to reflect this constraint.
Update README.md, AGENTS.md, and design docs to reflect the new subcommand interface (lnk <command> [args]) replacing action flags (-C/-R/-S/-P/-A/-O). Also refine PrintNextStep signature to include sourceDir, fix orphan dry-run formatting, and expand built-in ignore patterns list.
Use `ln -s source target` instead of `ln TARGET LINK_NAME` to match the conventional symlink creation syntax.
- adopt: empty directory argument is now a hard error (not silent no-op); add pre-execute source-exists check; roll back includes CleanEmptyDirs on parent dirs of failed adoptions - orphan: change empty-collection message to "No managed symlinks found." - remove/prune: add next-step hint to success summary - status: change empty-result message to "No managed links found."
…rror-handling specs - internals.md: CreateSymlink no longer prints output (caller's responsibility); add specs for ValidateSymlinkCreation, PatternMatcher, validateAdoptSource, LoadIgnoreFile; update CleanEmptyDirs usage (add adopt rollback); update related specs table - orphan.md: add home directory validation step (Phase 1) and corresponding error table row - output.md: PrintCommandHeader suppresses bold header when ShouldSimplifyOutput() - status.md: add exit code section (exits 0 on broken links, only 1 on failure); renumber remaining sections - config.md: clarify built-in ignore patterns can be negated by later entries - error-handling.md: clarify continue-on-failure pattern (all-or-nothing validation, then per-item execution); add "path outside home directory" row to orphan validation error table
Target directory is now always ~ (not configurable via CLI). Remove optional [target-dir] positional arg from create/remove/status/prune commands. Add TargetDir to AdoptOptions and OrphanOptions for testability. Drop --quiet/-q flag and reduce verbosity levels from three to two. Add deduplication notes to adopt and orphan specs.
…trategy Add stdlib.md documenting which stdlib packages and functions to use for directory traversal, symlink resolution, path operations, pattern matching, and file operations. Specifies WalkDir over Walk, EvalSymlinks for managed link detection, and the source-dir vs target-dir traversal table. Update internals.md to use WalkDir and EvalSymlinks in FindManagedLinks, add broken link fallback strategy, and note that remove no longer uses FindManagedLinks. Update remove.md to use source-dir walk (symmetric with create) instead of FindManagedLinks, with EvalSymlinks for managed link detection.
Adds a prompt and tracker for a three-role agent loop (Planner → Approver → Implementer) that autonomously finds and implements substantive improvements to the design specs. - .improvement-prompt.md: single prompt used for all three roles; role is determined by reading phase: from the tracker - .improvement-tracker.md: state machine tracking current phase, active suggestion, decisions, denied history, and full history
8fedc6f to
e02d9ed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.